﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TableParsers;
using System.Collections;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace SpeakupBackend.Themes
{
    /// <summary>
    /// ClassTypes that support themes to be assigned to them
    /// </summary>
    [Serializable]
    public sealed class WorkshopClassType: IEquatable<WorkshopClassType>, 
                                         IComparable, 
                                         IComparable<WorkshopClassType>, 
                                         ISerializable
    {
        static List<ClassType> s_validClassTypes = new List<ClassType>();
        static public IList<ClassType> ValidClassTypes 
        {
            get
            {
                return s_validClassTypes.AsReadOnly();
            }
        }

        static List<WorkshopClassType> AvailableClassTypes = new List<WorkshopClassType>();
        static Dictionary<ClassType, WorkshopClassType> ClassType2WorkshopClassType = new Dictionary<ClassType,WorkshopClassType>();

        static WorkshopClassType()
        {
            s_validClassTypes.Add(TableParsers.ClassType.Grammar);
            s_validClassTypes.Add(TableParsers.ClassType.Writing);
            s_validClassTypes.Add(TableParsers.ClassType.Reading);
            s_validClassTypes.Add(TableParsers.ClassType.Conversation);

            foreach(ClassType classType in ValidClassTypes)
            {
                WorkshopClassType workshopClassType = new WorkshopClassType(classType);
                AvailableClassTypes.Add(workshopClassType);
                ClassType2WorkshopClassType[classType] = workshopClassType;
            }
        }

        public static IList<WorkshopClassType> GetWorkshopClassTypes()
        {
            return AvailableClassTypes.AsReadOnly();
        }
        
        public static WorkshopClassType GetWorkshopClassType(ClassType classType)
        {
            if (ClassType2WorkshopClassType.Keys.Contains(classType) == false)
            {
                throw new ArgumentException("Specified class type does not have theme");
            }
            return ClassType2WorkshopClassType[classType];
        }

        public static WorkshopClassType GetWorkshopClassType(String strClassType)
        {
            ClassType classType;
            try
            {
                classType = (ClassType)Enum.Parse(typeof(ClassType), strClassType);
            }
            catch(Exception ex)
            { 
                throw new FormatException(String.Format("Value \"{0}\" was not recognized as valid ClassType.", strClassType), ex);            
            }

            return GetWorkshopClassType(classType);
        }
        
        public static bool IsValidClassType(TableParsers.ClassType classType)
        {
            return ValidClassTypes.Contains(classType);
        }

        readonly ClassType m_classType;
        public ClassType ClassType
        {
            get
            {
                return m_classType;
            }
        }

        private WorkshopClassType(ClassType classType)
        {
            if (!IsValidClassType(classType))
            {
                throw new ArgumentException("ClassType");
            }

            m_classType = classType;
        }

        public override bool Equals(object obj)
        {
            //if both referneces are the same
            if (Object.ReferenceEquals(this, obj))
            {
                return true;
            }

            //if 'obj' is null
            if (Object.ReferenceEquals(null, obj))
            {
                return false;
            }

            WorkshopClassType rhs = obj as WorkshopClassType;
            if (!Object.ReferenceEquals(null, rhs))
            {
                return Equals(rhs);
            }

            return false;
        }

        public bool Equals(WorkshopClassType other)
        {
            //if both referneces are the same
            if (Object.ReferenceEquals(this, other))
            {
                return true;
            }

            //if 'obj' is null
            if (Object.ReferenceEquals(null, other))
            {
                return false;
            }

            return CompareTo(other) == 0;
        }

        public override int GetHashCode()
        {
            return ClassType.GetHashCode();
        }

        public override string ToString()
        {
            return ClassType.ToString();
        }        

        public int CompareTo(Object obj)
        {
            return CompareTo((WorkshopClassType)obj);
        }

        public int CompareTo(WorkshopClassType other)
        {
            if (ClassType == other.ClassType)
            {
                return 0;
            }

            return (int)ClassType - (int)other.ClassType;
        }

        #region Comparison operators

        public static bool operator ==(WorkshopClassType rhs, WorkshopClassType lhs)
        {
            //if both are null, return true
            if (Object.ReferenceEquals(rhs, null) &&
                Object.ReferenceEquals(lhs, null))
            {
                return true;
            }

            //if only one of them is null, return false
            if (Object.ReferenceEquals(rhs, null) ||
                Object.ReferenceEquals(lhs, null))
            {
                return false;
            }

            return rhs.CompareTo(lhs) == 0;
        }

        public static bool operator !=(WorkshopClassType rhs, WorkshopClassType lhs)
        {
            return !(rhs == lhs);
        }

        public static bool operator <(WorkshopClassType rhs, WorkshopClassType lhs)
        {
            return rhs.CompareTo(lhs) < 0;
        }

        public static bool operator >(WorkshopClassType rhs, WorkshopClassType lhs)
        {
            return rhs.CompareTo(lhs) > 0;
        }

        public static bool operator <=(WorkshopClassType rhs, WorkshopClassType lhs)
        {
            return rhs.CompareTo(lhs) <= 0;
        }

        public static bool operator >=(WorkshopClassType rhs, WorkshopClassType lhs)
        {
            return rhs.CompareTo(lhs) >= 0;
        }

        public static implicit operator ClassType(WorkshopClassType workshopClassType)
        {
            return workshopClassType.ClassType;
        }

        #endregion

        #region Serialization

        static readonly string ClassTypeSerializationKey = "ClassType";

        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(SerializationHelper));
            info.AddValue(ClassTypeSerializationKey, ClassType);
        }

        [Serializable]
        private sealed class SerializationHelper : IObjectReference, ISerializable
        {
            ClassType ClassType;

            SerializationHelper(SerializationInfo info, StreamingContext context)
            {
                ClassType = (ClassType)info.GetValue(ClassTypeSerializationKey, typeof(ClassType));
            }

            Object IObjectReference.GetRealObject(StreamingContext context)
            {
                return WorkshopClassType.GetWorkshopClassType(ClassType);
            }

            void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
            {
                throw new NotImplementedException();
            }
        }

        #endregion
    }
}
